////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNCore/inc/WNTypes.h"
#include "WNMemory/inc/WNMemory.h"
#include "WNStrings/inc/WNStrings.h"
#include "WNScripting/inc/WNScriptLexerDefines.h"
#include "WNScripting/gen/WNScriptParser.h"

#define SETTOKEN(tokenType, string, length) \
    token->mId = tokenType; \
    token->mTokenString = s->mIdentifierMap->GetIdentifier(string, length); \
    token->mStartChar = s->mCharNo; \
    s->mCharNo += length; \
    token->mEndChar = s->mCharNo; \
    token->mStartLine = s->mLineNo; \
    token->mEndLine = s->mLineNo; 

#define YYCTYPE     WN_CHAR
#define YYCURSOR    (s->mStart)
#define YYLIMIT     (s->mEnd)
#define YYMARKER    (s->mMarker)

using namespace WNStrings;
using namespace WNMemory;
using namespace WNScripting;

WN_SIZE_T CountChars(WN_CHAR* text, WN_CHAR* endText) {
    WN_BOOL slash = WN_FALSE;
    WN_SIZE_T numChars = 0;

    while(text != endText) {

        if (*text == '\\') {
            slash = !slash;
        } else {
            if(slash) {
                slash = !slash;
            }
        }

        if(!slash) {
            numChars += 1;
        }

        text++;
    }

    return(numChars);
}

WN_CHAR GetCharFromText(WN_CHAR* text, WN_SIZE_T& inc) {
    if (*text != '\\') {
        inc = 1;

        return(*text);
    } else {
        inc = 2;

        switch(text[1]) {
            case 'n':
                return('\n');
            case 'b':
                return('\b');
            case 'r':
                return('\r');
            case 't':
                return('\t');
            case '\\':
                return('\\');
            case '\"':
                return('\"');
            case '\'':
                return('\'');
            case '0':
                return('\0');
            default:
                inc = 0;

                return(0);
        }
    }

    return(0);
}

WN_INT32 WNScriptLex(WNLexerInfo* s, WNScriptToken* token) {
    WN_INT32 r = 0;
    WN_CHAR* start = s->mStart;

    while(0 == r) {
/*!re2c
    re2c:indent:top = 1;
    re2c:indent:string = "  ";
    re2c:yyfill:enable = 0;

    "("     {
                SETTOKEN(WN_SCRIPT_LBRACKET, "(", 1);
                start = YYCURSOR;
                return(0);
            }
    ")"     {
                SETTOKEN(WN_SCRIPT_RBRACKET, ")", 1);
                start = YYCURSOR;
                return(0);
            }
    "{"     {
                SETTOKEN(WN_SCRIPT_LBRACE, "{", 1);
                return(0);
            }
    "}"     {
                SETTOKEN(WN_SCRIPT_RBRACE, "}", 1);
                start = YYCURSOR;
                return(0);
            }
    "["     {
                SETTOKEN(WN_SCRIPT_LSQBRACKET, "[", 1);
                start = YYCURSOR;
                return(0);
            }
    "]"     {
                SETTOKEN(WN_SCRIPT_RSQBRACKET, "]", 1);
                start = YYCURSOR;
                return(0);
            }
    ";"     {
                SETTOKEN(WN_SCRIPT_SEMICOLON, ";", 1);
                start = YYCURSOR;
                return(0);
            }
    "="     {
                SETTOKEN(WN_SCRIPT_EQUAL, "=", 1);
                start = YYCURSOR;
                return(0);
            }
    "+="    {
                SETTOKEN(WN_SCRIPT_PLUS_EQUAL, "+=", 2);
                start = YYCURSOR;
                return(0);
            }
    "*="    {
                SETTOKEN(WN_SCRIPT_TIMES_EQUAL, "*=", 2);
                start = YYCURSOR;
                return(0);
            }
    "%="    {
                SETTOKEN(WN_SCRIPT_MOD_EQUAL, "%=", 2);
                start = YYCURSOR;
                return(0);
            }
    "/="    {
                SETTOKEN(WN_SCRIPT_DIVIDE_EQUAL, "/=", 2);
                start = YYCURSOR;
                return(0);
            }
    "-="    {
                SETTOKEN(WN_SCRIPT_MINUS_EQUAL, "-=", 2);
                start = YYCURSOR;
                return(0);
            }
    "<=="   {
                SETTOKEN(WN_SCRIPT_COPY, "<==", 3);
                start = YYCURSOR;
                return(0);
            }

    "return"{
                SETTOKEN(WN_SCRIPT_RETURN, "return", 6);
                start = YYCURSOR;
                return(0);
            }
    "if"    {
                SETTOKEN(WN_SCRIPT_IF, "if", 2);
                start = YYCURSOR;
                return(0);
            }
    "else"  {
                SETTOKEN(WN_SCRIPT_ELSE, "else", 4);
                start = YYCURSOR;
                return(0);
            }
    "function" {
                    SETTOKEN(WN_SCRIPT_FUNCTION, "function", 8);
                    start = YYCURSOR;
                    return(0);
               }
    "Int"   {
                SETTOKEN(WN_SCRIPT_INTTYPE, "Int", 3);
                start = YYCURSOR;
                return(0);
            }
    "Float" {
                SETTOKEN(WN_SCRIPT_FLOATTYPE, "Float", 5);
                start = YYCURSOR;
                return(0);
            }
    "Char"  {
                SETTOKEN(WN_SCRIPT_CHARTYPE, "Char", 4);
                start = YYCURSOR;
                return(0);
            }
    "while" {
                SETTOKEN(WN_SCRIPT_WHILE, "while", 5);
                start = YYCURSOR;
                return(0);
            }
    "do"    {
                SETTOKEN(WN_SCRIPT_DO, "do", 2);
                start = YYCURSOR;
                return(0);
            }
    "for"   {
                SETTOKEN(WN_SCRIPT_FOR, "for", 3);
                start = YYCURSOR;
                return(0);
            }
    "#"     {
                SETTOKEN(WN_SCRIPT_ARRAY_LENGTH, "#", 1);
                start = YYCURSOR;
                return(0);
            }
    "+"     {
                SETTOKEN(WN_SCRIPT_PLUS, "+", 1);
                start = YYCURSOR;
                return(0);
            }
    "-"     {
                SETTOKEN(WN_SCRIPT_MINUS, "-", 1);
                start = YYCURSOR;
                return(0);
            }
    "/"     {
                SETTOKEN(WN_SCRIPT_DIVIDE, "/", 1);
                start = YYCURSOR;
                return(0);
            }
    "."     {
                SETTOKEN(WN_SCRIPT_DOT, ".", 1);
                start = YYCURSOR;
                return(0);
            }
    ","     {
                SETTOKEN(WN_SCRIPT_COMMA, ".", 1);
                start = YYCURSOR;
                return(0);
            }
    "*"     {
                SETTOKEN(WN_SCRIPT_TIMES, "*", 1);
                start = YYCURSOR;
                return(0);
            }
    "%"     {
                SETTOKEN(WN_SCRIPT_MOD, "%", 1);
                start = YYCURSOR;
                return(0);
            }
    "=="    {
                SETTOKEN(WN_SCRIPT_EQUALITY, "==", 2);
                start = YYCURSOR;
                return(0);
            }
    ">"     {
                SETTOKEN(WN_SCRIPT_GREATER, ">", 1);
                start = YYCURSOR;
                return(0);
            }
    "<"     {
                SETTOKEN(WN_SCRIPT_LESS, "<", 1);
                start = YYCURSOR;
                return(0);
            }
    ">="    {
                SETTOKEN(WN_SCRIPT_GREATERTHANOREQUAL, ">=", 2);
                start = YYCURSOR;
                return(0);
            }
    "<="    {
                SETTOKEN(WN_SCRIPT_LESSTHANOREQUAL, "<=", 2);
                start = YYCURSOR;
                return(0);
            }
    "!="    {
                SETTOKEN(WN_SCRIPT_INEQUALITY, "!=", 2);
                start = YYCURSOR;
                return(0);
            }
    "~"     {
                SETTOKEN(WN_SCRIPT_TILDE, "~", 2);
                start = YYCURSOR;
                return(0);
            }
    "new"   { 
                SETTOKEN(WN_SCRIPT_HEAP_ALLOC, "new", 3);
                start = YYCURSOR;
                return(0);
            }
    "delete"{
                SETTOKEN(WN_SCRIPT_HEAP_DELETE, "delete", 6);
                start = YYCURSOR;
                return(0);
            }
    "stackret"{
                    SETTOKEN(WN_SCRIPT_STACKRET, "stackret", 8);
                    start = YYCURSOR;
                    return(0);
              }
    "-"?([1-9][0-9]*|[0-9])  {
                SETTOKEN(WN_SCRIPT_INTEGER, start, YYCURSOR - start);
                std::string str(start, YYCURSOR);
                token->mIntConst = (WNS_INT_CONST)atoi(str.c_str());
                start = YYCURSOR;
                return(0);
            }

    ([1-9][0-9]*|[0-9])"."[0-9]* { 
                SETTOKEN(WN_SCRIPT_FLOAT, start, YYCURSOR - start);
                std::string str(start, YYCURSOR);
                token->mFloatConst = (WNS_FLOAT_CONST)atof(str.c_str());
                start = YYCURSOR;
                return(0);
            }

    "\'\\"."\'"{
                SETTOKEN(WN_SCRIPT_CHAR, start, YYCURSOR - start);
                WN_SIZE_T crap = 0;
                token->mCharConst = GetCharFromText(start + 1, crap);
                if(crap == 0){
                    char * lexed =  WNStrNDup(start, YYCURSOR - start);  
                    WNLexerReportError(s, "Lexing failed, invalid character constant", lexed, s->mLineNo, s->mCharNo); 
                    WNFree(lexed);
                }
                start = YYCURSOR;
                return(0);
            }

    "\'"."\'"{
                SETTOKEN(WN_SCRIPT_CHAR, start, YYCURSOR - start);
                WN_SIZE_T crap = 0;
                token->mCharConst = GetCharFromText(start + 1, crap);
                if (crap == 0) {
                        char * lexed =  WNStrNDup(start, YYCURSOR - start);  
                        WNLexerReportError(s, "Lexing failed, invalid character constant", lexed, s->mLineNo, s->mCharNo); 
                        WNFree(lexed);
                }
                start = YYCURSOR;
                return(0); 
            }

    "\""([^\"\\\']|"\\"[\"\\\'rtnb0])*"\""{
                SETTOKEN(WN_SCRIPT_STRING, start, YYCURSOR - start);
                WN_SIZE_T numchars = CountChars(start + 1, YYCURSOR - 1);
                WN_CHAR* cOut = (WN_CHAR*)WNMalloc(sizeof(WN_CHAR) * (numchars + 1));
                cOut[numchars] = '\0';
                start += 1;
                for(WN_SIZE_T i = 0; i < numchars; ++i){
                    WN_SIZE_T increment;
                    WN_CHAR c = GetCharFromText(start, increment);
                    start += increment;
                    if(increment == 0){
                        char * lexed =  WNStrNDup(start, YYCURSOR - start);  
                        WNLexerReportError(s, "Lexing failed, invalid string detected", lexed, s->mLineNo, s->mCharNo); 
                        WNFree(lexed);
                    }
                    cOut[i] = c;
                }
                token->mStringConst = cOut;
                return(0);
            }

    [\n]    { s->mLineNo += 1; start = YYCURSOR; s->mCharNo = 1; r = WN_SCRIPT_WHITESPACE; break;}
    " "     { s->mCharNo += 1; start = YYCURSOR; r = WN_SCRIPT_WHITESPACE; break;}
    "\t"    { s->mCharNo += WN_SCRIPTING_TAB_SIZE; start = YYCURSOR; r = WN_SCRIPT_WHITESPACE; break;}
    "\r"    { r = WN_SCRIPT_WHITESPACE; start = YYCURSOR; break; }

    [a-z][a-z|A-Z|0-9|_|-]* {
                                    SETTOKEN(WN_SCRIPT_IDENTIFIER, start, YYCURSOR - start);
                                    start = YYCURSOR;
                                    return(0);
                            }

    [A-Z][a-z|A-Z|0-9|_|-]* {
                                    SETTOKEN(WN_SCRIPT_USERTYPENAME, start, YYCURSOR - start);
                                    start = YYCURSOR;
                                    return(0);
                            }
    "\000"  { r = WN_SCRIPT_EOF;  break; }

    [^]     {
                r = WN_SCRIPT_ERR;
                char * lexed =  WNStrNDup(start, YYCURSOR - start);  
                WNLexerReportError(s, "Lexing failed, invalid sequence detected", lexed, s->mLineNo, s->mCharNo); 
                WNFree(lexed);
                break;
            }
*/
    }
    return r;
}
